use crate::sound::sound::SWEEP_DELAY_ZERO_PERIOD;

use super::{lengthcounter::LenghtCounter, sound::WAVE_PATTERN, volumeenvelope::VolumeEnvelope};
use blip_buf::BlipBuf;

pub(crate) struct SquareChannel {
    active: bool,
    dac_enabled: bool,

    duty: u8,
    phase: u8,
    length: LenghtCounter,

    frequency: u16,
    period: u32,
    last_amp: i32,
    delay: u32,
    has_sweep: bool,

    sweep_enabled: bool,
    sweep_frequency: u16,
    sweep_delay: u8,
    sweep_period: u8,
    sweep_shift: u8,
    sweep_negate: bool,

    sweep_did_negate: bool,
    pub(crate) volume_envelope: VolumeEnvelope,

    pub(crate) blip: BlipBuf,
}

impl SquareChannel {
    pub(crate) fn new(blip: BlipBuf, with_sweep: bool) -> Self {
        Self {
            active: false,
            dac_enabled: false,
            duty: 1,
            phase: 1,
            length: LenghtCounter::new(64),
            frequency: 0,
            period: 2048,
            last_amp: 0,
            delay: 0,
            has_sweep: with_sweep,
            sweep_enabled: false,
            sweep_frequency: 0,
            sweep_delay: 0,
            sweep_period: 0,
            sweep_shift: 0,
            sweep_negate: false,
            sweep_did_negate: false,
            volume_envelope: VolumeEnvelope::new(),
            blip: blip,
        }
    }

    pub(crate) fn on(&self) -> bool {
        self.active
    }

    pub(crate) fn rb(&self, addr: u16) -> u8 {
        match addr {
            0xFF10 => {
                let tmp1 = 0x80;
                let tmp2 = (self.sweep_period & 0x7) << 4;
                let tmp3 = if self.sweep_negate { 0x8 } else { 0 };
                let tmp4 = self.sweep_shift & 0x7;

                tmp1 | tmp2 | tmp3 | tmp4
            }

            0xFF11 | 0xFF16 => {
                let tmp1 = (self.duty & 3) << 6;
                let tmp2 = 0x3F;
                tmp1 | tmp2
            }

            0xFF12 | 0xFF17 => self.volume_envelope.rb(addr),

            0xFF13 | 0xFF18 => 0xFF,

            0xFF14 | 0xFF19 => {
                let tmp1 = 0x80;
                let tmp2 = if self.length.enabled { 0x40 } else { 0 };
                let tmp3 = 0x3F;
                tmp1 | tmp2 | tmp3
            }
            _ => unimplemented!("not support addr"),
        }
    }

    pub(crate) fn wb(&mut self, addr: u16, value: u8, frame_step: u8) {
        match addr {
            0xFF10 => {
                self.sweep_period = (value >> 4) & 0x7;
                self.sweep_shift = value & 0x7;
                let old_sweep_nagate = self.sweep_negate;

                if old_sweep_nagate && !self.sweep_negate && self.sweep_did_negate {
                    self.active = false;
                }
                self.sweep_did_negate = false;
            }

            0xFF11 | 0xFF16 => {
                self.duty = value >> 6;
                self.length.set(value & 0x3F);
            }

            0xFF12 | 0xFF17 => {
                self.dac_enabled = value & 0xF8 != 0;
                self.active = self.active && self.dac_enabled;
            }

            0xFF13 | 0xFF18 => {
                self.frequency = (self.frequency & 0x0700) | (value as u16);
            }

            0xFF14 | 0xFF19 => {
                self.frequency = (self.frequency & 0x00FF) | (((value & 0b0000_0111) as u16) << 8);
                self.calculate_period();

                self.length.enable(value & 0x40 == 0x40, frame_step);
                self.active &= self.length.is_active();

                if value & 0x80 == 0x80 {
                    if self.dac_enabled {
                        self.active = true;
                    }

                    self.length.trigger(frame_step);

                    if self.has_sweep {
                        self.sweep_frequency = self.frequency;
                        self.sweep_delay = if self.sweep_period != 0 {
                            self.sweep_period
                        } else {
                            SWEEP_DELAY_ZERO_PERIOD
                        };

                        self.sweep_enabled = self.sweep_period > 0 || self.sweep_shift > 0;

                        if self.sweep_shift > 0 {
                            self.sweep_calculate_frequency();
                        }
                    }
                }
            }

            _ => unimplemented!("not impl yet"),
        }

        self.volume_envelope.wb(addr, value);
    }

    fn calculate_period(&mut self) {
        if self.frequency > 2047 {
            self.period = 0;
        } else {
            self.period = (2048 - self.frequency as u32) * 4;
        }
    }

    pub(crate) fn run(&mut self, start_time: u32, end_time: u32) {
        if !self.active || self.period == 0 {
            if self.last_amp != 0 {
                self.blip.add_delta(start_time, -self.last_amp);
                self.last_amp = 0;
                self.delay = 0;
            }
        } else {
            let mut time = start_time + self.delay;
            let pattern = WAVE_PATTERN[self.delay as usize];
            let vol = self.volume_envelope.volume as i32;

            while time < end_time {
                let amp = vol * pattern[self.phase as usize];
                if amp != self.last_amp {
                    self.blip.add_delta(time, amp - self.last_amp);
                    self.last_amp = amp;
                }

                time += self.period;
                self.phase = (self.phase + 1) % 8;
            }

            // next time, we have to wait
            // an additional delay timesteps
            self.delay = time - end_time;
        }
    }

    pub(crate) fn step_length(&mut self) {
        self.length.step();
        self.active &= self.length.is_active();
    }

    fn sweep_calculate_frequency(&mut self) -> u16 {
        let offset = self.sweep_frequency >> self.sweep_shift;

        let newfreq = if self.sweep_enabled {
            self.sweep_did_negate = true;
            self.sweep_frequency.wrapping_sub(offset)
        } else {
            self.sweep_frequency.wrapping_add(offset)
        };

        if newfreq > 2047 {
            self.active = false;
        }

        return newfreq;
    }

    pub(crate) fn step_sweep(&mut self) {
        // debug
        debug_assert!(self.has_sweep);

        if self.sweep_delay > 1 {
            self.sweep_delay -= 1;
        } else {
            if self.sweep_period == 0 {
                self.sweep_delay = SWEEP_DELAY_ZERO_PERIOD;
            } else {
                self.sweep_delay = self.sweep_period;
                if self.sweep_enabled {
                    let newfreq = self.sweep_calculate_frequency();

                    if newfreq <= 2047 {
                        if self.sweep_shift != 0 {
                            self.sweep_frequency = newfreq;
                            self.frequency = newfreq;
                            self.calculate_period();
                        }

                        self.sweep_calculate_frequency();
                    }
                }
            }
        }
    }
}
